package com.rt.gptnext.common.utils.http;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.rt.gptnext.common.constant.Constants;
import com.rt.gptnext.common.constant.HttpConstants;
import com.rt.gptnext.common.utils.http.validator.JsonValidator;
import lombok.Getter;
import lombok.SneakyThrows;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author gaoyiyang
 **/
@Getter
public class HttpRequest {

	private final Map<String, String> headers = new HashMap<>(16);
	private final Map<String, String> cookies = new HashMap<>(16);
	/**
	 * multipart表单
	 */
	private final List<Multipart> multipartList = new ArrayList<>();
	/**
	 * 请求分组
	 */
	@Getter
	private String group = "default";
	/**
	 * 接口名
	 */
	private String apiName;
	/**
	 * 业务ID
	 */
	private Serializable businessId;
	private String url;
	private String method = HttpConstants.GET;
	private Map<String, Object> form = new HashMap<>(16);
	private String body;

	/**
	 * 是否下载
	 */
	private boolean download;

	/**
	 * 输出流
	 */
	private OutputStream out;

	/**
	 * 请求校验器
	 */
	private HttpSuccessValidator validator = HttpSuccessValidator.DEFAULT;

	/**
	 * 是否输出日志
	 */
	private boolean printLog = true;

	public static HttpRequest create(String url) {
		return new HttpRequest()
			.url(url);
	}

	/**
	 * 对象转换为字符串，用于URL的Query中
	 *
	 * @param value 值
	 * @return 字符串
	 */
	private static String toStr(Object value) {
		String result;
		if (value instanceof Iterable) {
			result = CollUtil.join((Iterable<?>) value, ",");
		} else if (value instanceof Iterator) {
			result = IterUtil.join((Iterator<?>) value, ",");
		} else {
			result = Convert.toStr(value);
		}
		return result;
	}

	/**
	 * 分组
	 */
	public HttpRequest group(String group) {
		this.group = group;
		return this;
	}

	/**
	 * 是否输出日志
	 */
	public HttpRequest printLog(boolean printLog) {
		this.printLog = printLog;
		return this;
	}

	/**
	 * 设置请求方法
	 */
	public HttpRequest method(String method) {
		this.method = method;
		return this;
	}

	/**
	 * 设置请求校验器
	 *
	 * @param validator 请求校验器
	 * @return -
	 */
	public HttpRequest validator(HttpSuccessValidator validator) {
		this.validator = validator;
		return this;
	}

	/**
	 * 设置请求校验器
	 *
	 * @param validator 请求校验器
	 * @return -
	 */
	public HttpRequest jsonValidator(JsonValidator validator) {
		this.validator = validator;
		return this;
	}

	public HttpRequest get() {
		return method(HttpConstants.GET);
	}

	public HttpRequest post() {
		return method(HttpConstants.POST);
	}

	public HttpRequest put() {
		return method(HttpConstants.PUT);
	}

	public HttpRequest delete() {
		return method(HttpConstants.DELETE);
	}

	/**
	 * 设置接口名
	 *
	 * @param apiName 接口名
	 * @return 当前请求对象
	 */
	public HttpRequest apiName(String apiName) {
		this.apiName = apiName;
		return this;
	}

	/**
	 * 设置业务ID
	 *
	 * @param businessId 业务ID
	 * @return 当前请求对象
	 */
	public HttpRequest businessId(Long businessId) {
		this.businessId = businessId;
		return this;
	}

	/**
	 * 设置请求路径
	 */
	public HttpRequest url(String url) {
		this.url = url;
		return this;
	}

	/**
	 * 设置请求表单
	 *
	 * @param form 请求表单
	 * @return -
	 */
	public HttpRequest form(Map<String, Object> form) {
		this.form = form;
		return this;
	}

	/**
	 * 设置请求表单
	 *
	 * @param name  参数名
	 * @param value 参数值
	 * @return -
	 */
	public HttpRequest form(String name, Object value) {
		this.form.put(name, value);
		return this;
	}

	/**
	 * 上传文件
	 *
	 * @param name        参数名
	 * @param file        文件，可以是文件对象、输入流、字节数组
	 * @param contentType 类型
	 * @return -
	 */
	public HttpRequest file(String name, String fileName, Object file, String contentType) {
		return multipart(name, fileName, file, true, contentType);
	}

	public HttpRequest file(String name, Object file, String contentType) {
		return multipart(name, null, file, true, contentType);
	}

	public HttpRequest file(String name, Object file) {
		return multipart(name, null, file, true, null);
	}

	public HttpRequest multipart(String name, String fileName, Object value, boolean isFile) {
		return multipart(name, fileName, value, isFile, null);
	}

	public HttpRequest multipart(String name, Object value) {
		return multipart(name, null, value, false, null);
	}

	public HttpRequest multipart(String name, Object value, String contentType) {
		return multipart(name, null, value, false, contentType);
	}

	/**
	 * multipart表单
	 *
	 * @param name        参数名
	 * @param value       参数值
	 * @param contentType 类型
	 * @return -
	 */
	public HttpRequest multipart(String name, String fileName, Object value, boolean isFile, String contentType) {
		if (value == null) {
			value = Constants.EMPTY;
		}
		byte[] byteValue = new byte[0];
		if (value instanceof byte[]) {
			byteValue = (byte[]) value;
		} else if (value instanceof File) {
			if (FileUtil.exist((File) value)) {
				byteValue = FileUtil.readBytes((File) value);
				isFile = true;
				if (fileName == null) {
					fileName = ((File) value).getName();
				}
			}
		} else if (value instanceof InputStream) {
			byteValue = IoUtil.readBytes((InputStream) value);
			isFile = true;
		} else if (value instanceof String) {
			byteValue = ((String) value).getBytes();
		} else {
			byteValue = JSONUtil.toJsonStr(value).getBytes();
		}
		this.multipartList.add(new Multipart(name, fileName, byteValue, isFile, contentType));
		return this;
	}

	/**
	 * 设置请求头
	 *
	 * @param name  参数名
	 * @param value 参数值
	 * @return -
	 */
	public HttpRequest header(String name, String value) {
		this.headers.put(name, value);
		return this;
	}

	public HttpRequest authBearer(String token) {
		return header("Authorization", "Bearer " + token);
	}

	public HttpRequest cookie(String name, String value) {
		this.cookies.put(name, value);
		return this;
	}

	/**
	 * 设置其扭头
	 *
	 * @param headers 请求头
	 * @return -
	 */
	public HttpRequest headers(Map<String, String> headers) {
		this.headers.putAll(headers);
		return this;
	}

	/**
	 * 设置请求体
	 *
	 * @param body 请求体
	 * @return -
	 */
	public HttpRequest body(Object body) {
		if (body == null) {
			return this;
		}
		if (body instanceof String) {
			this.body = (String) body;
		} else {
			this.body = JSONUtil.toJsonStr(body);
		}
		return this;
	}

	/**
	 * 将表单参数设置到请求体
	 *
	 * @return -
	 */
	public HttpRequest form2Body() {
		if (form != null) {
			// 拼接请求url
			StringBuilder formString = new StringBuilder();
			form.forEach((k, v) ->
				formString.append(k).append("=").append(URLUtil.encodeAll(toStr(v))).append("&")
			);
			this.body = formString.substring(0, formString.length() - 1);
			this.header("Content-Type", "application/x-www-form-urlencoded");
			form.clear();
		}
		return this;
	}

	/**
	 * 是否下载
	 *
	 * @param download 下载
	 * @return -
	 */
	public HttpRequest download(boolean download) {
		this.download = download;
		return this;
	}

	/**
	 * 下载到输出流
	 *
	 * @param out 输出流
	 * @return -
	 */
	public HttpRequest download(OutputStream out) {
		this.out = out;
		this.download = true;
		return this;
	}

	public HttpResponse<byte[]> call(HttpEngine engine) {
		return engine.request(this);
	}

	public <T> HttpResponse<T> call(HttpEngine engine, Class<T> targetClass) {
		return engine.request(this).to(targetClass);
	}

	public HttpResponse<byte[]> call() {
		return call(HttpEngineManager.getGlobalEngine());
	}

	public <T> HttpResponse<T> call(Class<T> targetClass) {
		return call(HttpEngineManager.getGlobalEngine(), targetClass);
	}

	public void stream(HttpEngine engine, Consumer<HttpStream> consumer) {
		engine.requestStream(this, consumer, null);
	}

	public void stream(Consumer<HttpStream> consumer) {
		stream(HttpEngineManager.getGlobalEngine(), consumer);
	}

	public StreamReader streamRead(HttpEngine engine, Function<String, Boolean> function, boolean readLine) {
		var streamReader = new StreamReader();
		engine.requestStream(this, stream -> stream.read(function, readLine), streamReader.block);
		return streamReader;
	}

	public StreamReader streamRead(HttpEngine engine, Function<String, Boolean> function) {
		return streamRead(engine, function, false);
	}

	public StreamReader streamRead(Function<String, Boolean> function, boolean readLine) {
		return streamRead(HttpEngineManager.getGlobalEngine(), function, readLine);
	}

	public StreamReader streamRead(Function<String, Boolean> function) {
		return streamRead(HttpEngineManager.getGlobalEngine(), function);
	}

	public static class StreamReader {

		private final CountDownLatch block = new CountDownLatch(1);

		@SneakyThrows
		public void blocking() {
			block.await();
		}

	}

}
